干货 | 一文带你了解携程第四代全链路测试系统
作者简介
康猛,携程网站运营中心资深技术支持工程师,在互联网基础架构系统设计,后端开发,性能测试领域有多年实践经验。喜欢钻研新技术,转化研究成果,提升工作效率。
一、背景与意义
携程的应用性能测试和容量评估从技术思路上,历经了三代产品:
早期的应用性能测试主要依赖一些成熟的测试工具,如ab、Jmeter等,人工构造有限集合的简单报文,对测试环境或者生产环境的某一个接口,施加一定压力进行测试。用于分析在一定负载压力下,单一应用有限数量接口的CPU、内存、响应时间等指标的性能表现。
该方法在一定程度上可以模拟较大的压力输入,但往往难以构造复杂的、高仿真生产的海量用户输入的场景,更别提需要模拟生产环境真实应用间相互依赖的场景了。
为了解决第一代产品中海量复杂用户输入的问题,携程开发了第二代单应用生产环境容量评估工具。
其基本原理是提高应用所在集群中某台机器的权重,使其承担远高于其他机器的真实流量负载,进而分析在该负载下,测试机应用的性能表现。
该方法在一定程度上解决了海量真实用户输入的问题,用真实的、复杂的业务场景对生产应用进行压力测试与容量评估,这对原本就有较高流量的应用也许有效,但对于正常情况下流量不大,仅在大促场景下负载爆发的应用场景,比较难以奏效。且测试机往往拥有较高的机器权重以承担较高流量,当单机故障或者冒烟,影响应用健康状态的比重就加大,收益与风险成正比。
此外,该方法通过调整单机权重使单机流量突增,整个集群的负载并没有变化,对集群上下游与中间件的负载也没有变化,这就很难测试出当前应用流量突增会不会对上下游依赖产生什么影响,应用容量的评估也很难准确。
生产环境流量回放的基本原理是对应用所在集群中,流经一台或多台机器的流量进行拷贝,并以实时的方式将流量的副本分发到测试机上,同时也可以将流量的镜像拷贝保存为副本文件,随时随地的对副本进行离线回放。
回放时支持对回放流量进行比例加压,比如加压10倍回放以模拟超高负载输入。此外,回放时还支持对回放流量进行过滤以及修改等操作,可以过滤仅回放某个固定URL,也可以在回放时修改某接口的参数等高级功能。
该方法完美解决了海量用户输入的问题,使用真实的业务流量,不仅支持实时的AB流量对比,也支持离线的高压副本回放,解决了整个集群的流量输入的巨大变化,并且可以在一定程度上模拟单应用负载骤增下,上下游依赖应用的性能评估。
但是当生产链路过于复杂,使用流量回放思路去模拟也会变得复杂,且集中式的回放难以匹配超高并发,如数十万并发场景下的压测需求。
总结下来,三代技术产品思路在设计之初,都不可避免的需要回答一个基础问题:生产还是测试?
性能测试的设计初衷就是要探究生产应用的容量极限,在这一过程中,常常会伴随着应用冒烟或者进程奔溃,测试环境天然能够隔离故障,可以确保测试数据以及测试流量不会对生产业务产生影响。
但测试环境往往采用利旧设备,其设备多为过保机器,无法保证可用性,无法拟合生产机器的性能表现,这就导致测试数据往往不准,且测试环境上下游依赖数据往往缺失,无法真实模拟生产海量用户输入,可能导致测试数据与真实出现较大偏差。
生产环境天然有数据场景丰富,依赖齐全以及配置完整的优势,压测可以获取真实业务性能表现,具有极大的参考价值。基于此,我们最终选择了生产环境作为系统设计的基础环境。
但由于在生产环境做测试容易产生脏数据以及因极限测试导致的故障,设计时需要着重考虑脏数据清除以及应用测试冒烟点的识别与熔断。
正是基于对以上问题的分析,我们设计并实现了携程第四代全链路测试系统,该系统:
①采用多种方式构造测试报文,尽可能的拟合生产业务数据输入;
②使用生产环境的真实业务逻辑,拟合应用上下游与中间件依赖,能够测试出单应用负载变化场景下对上下游压力的变化;
③压测过程中实时追踪压测路径,获取压测调用链与真实核心调用链的区别对压测路径进行优化调整,使性能测试结果趋于准确;
④压测流量可渐进增长,避免瞬时集中压力对应用产生不可预知的影响;
⑤支持超高压力的并发,模拟应用在超高压力下的性能表现。
二、系统设计
2.1、系统总体设计
系统总体采用分层设计的思路,主要包括数据构造层、压测逻辑层、控制层、引擎层、应用层、中间件等。
数据构造层主要解决压测数据构造的问题,有多种数据构造的方式可供选择,支持人工构造、请求日志埋点、流量回放等;
压测逻辑层主要解决压测任务设置以及压测脚本管理等问题,在这一层进行任务管理,如任务开始、终止、监控等,以及压测脚本的关联以及脚本库、依赖库的管理;
控制层的主要功能是任务下发、资源下发以及统计数据的回收等;
引擎层由数量众多的压测引擎构成,根据任务设置的并发开启相应数量的进程,每个进程中开启多线程的方式模拟高并发,对线上应用进行较大压力源输入;
应用层和中间件是我们生产环境真实有相互依赖关系的应用组以及其依赖的中间件;
整个系统设计主要以大促作为主要背景,模拟真实生产数据以及依赖发起较大压力的性能测试,支持进行高峰容量预测,获取真实业务容量以及核心调用链性能短板。
2.2、数据构造模块设计
数据构造模块可以支持三种数据构造方法:人工构造、日志埋点及生产流量回放。
根据以往经验,人工构造数据适用于请求报文较简单的场景,构造报文中的用户、供应商、产品、订单等字段需要做一定的参数化替换;获取生产埋点数据可以获取海量真实用户输入,适用于较复杂的测试场景,其时间字段,用户字段等需要额外替换或随机,同理也适用于生产流量的回放,数据构造参数化的基本原则是构造尽可能分散的、足够数量的虚拟用户、商户等数据,避免热点过于集中的问题。
构造的数据作为资源依赖连同压测脚本通过GIT做版本管理,将压测数据与压测平台解耦,方便用户定制适用于特定场景的压测方案。
2.3、全链路请求识别
既然是全链路压测,必然涉及到全链路的请求追踪问题,我们的实现方案是,在请求的入口侧识别是否有根节点的标记。
这个标记是一个http header,如果已经存在相应的root根节点标记,则透传这一标记,同时生成自己的current节点ID,并将上层的节点ID赋予Parent父标记;如果未发现相应的root根节点标记,则当前节点生成root根节点标记,并在后续请求调用中透传。
如此一来,每个节点都会有Root/Parent/Current三种标记类型,其中Root根节点标记是全局唯一的,如果每个节点都可以这样自我描述,就可以画出一张基于请求粒度全网的调用拓扑图,正是通过这种方法完成全链路压测的请求识别。
2.4、压测数据清理
在生产环境做全链路压测,不可避免会产生一些脏数据,压测结束后,需要清理这些脏数据,否则可能会污染后续的订单预测,PV/UV报表等,还可能会对生产环境的推荐算法产生误导。
通过压测前梳理生产核心链路,我们总结下来,需要清理的数据源主要有以下维度:BI数据、UBT数据、风控数据以及压测调用链上应用的落地数据等,一般情况下数据的清理都是基于构造的压测数据进行的,如根据构造的用户,构造的订单等。
2.5、全链路压测平台设计
我们总结下来,全链路压测平台应包含以下基本功能:
(1) 管理压测脚本
压测场景通过压测脚本表达,复杂的上下文逻辑以及参数化的随机可以通过脚本进行定制,平台支持查看/添加/删除/更新压测脚本,对脚本进行语法检查等,原始脚本全部存储在GIT上进行版本控制,平台可以添加GIT仓库地址批量管理脚本,也可以在脚本更新后刷新GIT仓库获取更新后的脚本。
(2) 管理压测主机
压测控制台分发压测任务到压测主机,压测主机通过开启多进程,每个进程开启多线程的方式构造足够数量的并发对目标应用进行压测。压测平台可以添加/删除压测主机,并接收压测主机回传的压测统计数据。
(3) 管理压测任务
通过平台可以创建/开始/终止压测任务,获取压测的真实链路,并查看本次压测的实时监控数据和统计数据等,当核心链路上出现应用冒烟,支持通过自动/人工方式终止压测任务。
(4) 生成压测报告
压测结束后,支持基于压测过程的统计和监控数据,自助填写压测报告,归档用于长期分析。
2.6、全链路监控设计
在生产环境做全链路压测,需要格外注意应用的实时的监控数据,我们总结,需包含以下维度:
(1) 机器维度
机器维度的监控数据主要包含:CPU使用率、CPU Load、内存使用率、连接数、网络吞吐、GC频率等指标,当出现应用内多台机器CPU等指标非线性变化,应格外注意任务的执行情况与应用的报错情况,及时的进行任务熔断。
(2) 应用维度
应用维度的监控数据主要包含应用请求量、报错量及响应时间等指标,当出现报错量的增加,或者响应时间的剧烈变化,应及时终止压测;
(3) 容量维度
容量维度的监控主要用于分析当前应用是否已经达到理论的容量上限,用于佐证压测效果、指导自动扩容等。
(4) 订单维度
订单维度的分析主要用于从订单角度进行故障发现,如果有核心链路存在监控遗漏的情况,则订单维度的监控是非常重要的补充,但是由于订单监控往往滞后于应用冒烟,所以当订单预测出现告警,则应及时有效的熔断压测任务。
(5) 链路检测
监控系统需要能够识别出压测请求所经过的应用调用链以及Redis、DB、Queue等中间件依赖,并汇总调用链上的监控数据,基本原则是“宁滥勿缺”,监控范围可以合理的扩展,避免出现监控盲区。
(6) 熔断控制
单一维度的监控很难反应生产应用的真实负载,全链路压测过程往往需要横跨多个维度,当出现异常点,应能够及时准确的判断问题所在,已经识别为异常点的,或者短时间不能快速定位异常情况,应及时有效的熔断压测任务,待异常分析确认结果后,决定是否继续进行压测任务。
2.7、全链路压测重点与难点
我们结合以往生产环境进行的多次压测任务,总结生产环境进行全链路压测工作的重难点工作:
(1) 重点工作
①生产核心业务调用链的预梳理,并不一定要求非常精确,用于快速判断压测可能存在的异常,并进行重点监控部署,分析压测是否符合预期等。
②构造符合条件的压测数据,这直接影响压测的效果,以及后续清理数据的复杂度,也往往占用比较大的工作量,是整个任务的重点工作。
③隔离压测数据,生产环境压测不可避免会有脏数据产生,这些数据可能会影响BI报表与业务推荐算法,需要在压测后及时清理,这部分也是压测任务的重点工作。
(2) 难点工作
① 压测数据的打标与识别,应用间调用需要将压测数据透传给后端,后端需要根据一定的标识识别出这是一次压测请求,并决定是否作出Mock等特殊的处理,不同应用间传递压测数据的方式并不完全一致,这可能涉及到一定的改造工作量。
② 多部门协作保证脏数据不会对生产业务产生影响。
三、案例分享
3.1、压测目标应用准备
(1) 链路梳理,获取压测核心链路;
(2) 应用改造,识别/打标/透传压测流量;
(3) 对外Mock,外部供应商的调用,以及短信邮件等,需要统一Mock。
3.2、压测数据准备
结合业务的具体特点,构造相应的压测数据:
3.3、压测链路识别与抽象
压测过程中,需要能够根据打标,获取请求维度的调用链路,并以此对全链路进行监控。
3.4、压测过程
(1) 创建压测场景
(2) 开始施压
(3) 压测监控
(4) 冒烟举例
当应用出现瓶颈的一般表现是请求出现排队,应用响应变慢,此时从服务端能看到响应时间的增加以及队列长度的变化,从客户端看到的现象是请求并发的下降,当看到TPS指标剧烈变化,一般表示服务端此时应用已经冒烟,需要紧急介入排查冒烟点。
3.5、压测总结
至此,从开启一段压测的角度对全链路测试过程进行总结如下:
(1) 压测前:了解链路,准备数据;
(2) 压测中:下发任务,监控链路;
(3) 压测后:清理数据,整理报告。
四、总结与展望
当前,携程全链路压测平台已经投产,并历经了多次压测考验,这一过程中,我们也进行了多次优化改版,后续将在降低使用费力度以及监控集成方面持续发力,提供用户更便捷使用体验、更加强大的全链路性能压测产品。
【推荐阅读】